package com.smartleanx.module.hrm.service.salarynotice;

import cn.hutool.core.collection.CollUtil;
import com.smartleanx.framework.common.enums.BizTypeEnum;
import com.smartleanx.framework.common.pojo.PageResult;
import com.smartleanx.framework.common.util.collection.CollectionUtils;
import com.smartleanx.framework.common.util.json.JsonUtils;
import com.smartleanx.framework.common.util.object.BeanUtils;
import com.smartleanx.framework.customfield.core.service.CustomFieldDataService;
import com.smartleanx.framework.customfield.core.service.bo.CustomFieldDataSaveBO;
import com.smartleanx.framework.customfield.core.service.bo.inner.CustomFieldDataBaseBO;
import com.smartleanx.module.hrm.context.EmployeeContextHolder;
import com.smartleanx.module.hrm.controller.app.salarynotice.vo.AppSalaryNoticePageReqVO;
import com.smartleanx.module.hrm.controller.app.salarynotice.vo.AppSalaryNoticeSaveReqVO;
import com.smartleanx.module.hrm.dal.dataobject.employee.EmployeeDO;
import com.smartleanx.module.hrm.dal.dataobject.salarynotice.SalaryNoticeDO;
import com.smartleanx.module.hrm.dal.mysql.salarynotice.SalaryNoticeMapper;
import com.smartleanx.module.hrm.intergration.NotifyMessageServiceProvider;
import com.smartleanx.module.hrm.service.employee.EmployeeService;
import com.smartleanx.module.hrm.service.payroll.PayrollDetailService;
import com.smartleanx.module.hrm.service.salarynotice.bo.SalaryNoticeSaveBO;
import com.smartleanx.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.smartleanx.module.system.enums.customfield.CustomFieldLabelEnum;
import com.smartleanx.module.system.enums.notify.NotifyDetailTypeEnum;
import com.smartleanx.module.system.enums.notify.NotifyTemplateCodeEnum;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;

import static com.smartleanx.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.smartleanx.module.hrm.enums.ErrorCodeConstants.*;

/**
 * 员工工资条通知 Service 实现类
 *
 * @author zhangchp
 */
@Service
@Validated
public class SalaryNoticeServiceImpl implements SalaryNoticeService {

    @Resource
    private SalaryNoticeMapper salaryNoticeMapper;

    @Resource
    private CustomFieldDataService customFieldDataService;

    @Lazy
    @Resource
    private PayrollDetailService payrollDetailService;

    @Resource
    private EmployeeService employeeService;

    @Resource
    private NotifyMessageServiceProvider notifyMessageServiceProvider;

    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public void createNotice(Collection<SalaryNoticeSaveBO> saveBOs, Collection<CustomFieldDataBaseBO> fieldDataList) {
        // 插入
        List<SalaryNoticeDO> insertObjs = BeanUtils.toBean(saveBOs, SalaryNoticeDO.class);
        salaryNoticeMapper.insertBatch(insertObjs);
        Map<Long, Long> idMap = CollectionUtils.convertMap(insertObjs, SalaryNoticeDO::getPayrollDetailId, SalaryNoticeDO::getId);
        fieldDataList.forEach(data -> data.setRecordId(idMap.get(data.getRecordId())));
        customFieldDataService.crudData(CustomFieldDataSaveBO.builder().datas(fieldDataList).bizType(BizTypeEnum.PAYROLL.getValue())
                .edition(BizTypeEnum.PAYROLL_SALARY_NOTICE.getValue()).labels(CollectionUtils.convertSet(fieldDataList, CustomFieldDataBaseBO::getLabel))
                .labelGroups(CollectionUtils.convertSet(fieldDataList, CustomFieldDataBaseBO::getLabelGroup))
                .recordIds(CollectionUtils.convertSet(fieldDataList, CustomFieldDataBaseBO::getRecordId)).build());
        notifyMessageServiceProvider.sendSingleNotifyBatch(CollectionUtils.convertList(employeeService.getEmployeeList(
                CollectionUtils.convertList(insertObjs, SalaryNoticeDO::getEmployeeId)), employee ->
                NotifySendSingleToUserReqDTO.builder().userId(employee.getUserId()).templateCode(NotifyTemplateCodeEnum.SALARY_CONFIRM_NOTICE.getCode())
                        .templateParams(Collections.emptyMap()).build()));
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class) // 添加事务，异常则回滚所有导入
    public void updateNotice(AppSalaryNoticeSaveReqVO updateReqVO) {
        // 校验存在
        validateNoticeExists(updateReqVO.getId());
        Long employeeId = EmployeeContextHolder.getEmployeeId();
        if(Objects.isNull(employeeId)){
            throw exception(SALARY_NOTICE_EMPLOYEE_UNBIND);
        }
        validateNoticeIdAndEmployeeIdConsistence(updateReqVO.getId(), employeeId);
        if(!updateReqVO.getConfirmStatus()){
            validateNoticeNotConfirm(updateReqVO.getId());
        }
        // 更新
        SalaryNoticeDO updateObj = BeanUtils.toBean(updateReqVO, SalaryNoticeDO.class);
        salaryNoticeMapper.updateById(updateObj);
        SalaryNoticeDO notice = salaryNoticeMapper.selectById(updateReqVO.getId());
        payrollDetailService.updateConfirmStatus(notice.getPayrollDetailId(), updateObj.getConfirmStatus(), updateObj.getFeedback());
        if(Boolean.FALSE.equals(updateReqVO.getConfirmStatus())){
            EmployeeDO employee = employeeService.getEmployeeById(employeeId);
            notifyMessageServiceProvider.sendSingleMessageToAdmin(Long.valueOf(notice.getCreator()), NotifyTemplateCodeEnum.SALARY_FEEDBACK_NOTICE.getCode(),
                    JsonUtils.toJsonString(Collections.singletonMap("employeeName", employee.getName())), NotifyDetailTypeEnum.PAYROLL_SLIP.getCode(), Collections.emptyMap());
        }
    }

    @Override
    public void deleteNoticeByPayrollDetailId(Long payrollDetailId) {
        // 校验
        validateNoticeNotConfirmByPayrollDetailId(payrollDetailId);
        // 删除
        salaryNoticeMapper.deleteByPayrollDetailId(payrollDetailId);
        customFieldDataService.crudData(CustomFieldDataSaveBO.builder().bizType(BizTypeEnum.PAYROLL.getValue())
                .edition(BizTypeEnum.PAYROLL_SALARY_NOTICE.getValue()).labels(CollUtil.newArrayList(CustomFieldLabelEnum.PAYROLL_EMPLOYEE_INFO.getValue(),
                        CustomFieldLabelEnum.PAYROLL_DETAIL_INFO.getValue(), CustomFieldLabelEnum.PAYROLL_SLIP_INFO.getValue()))
                .labelGroups(Collections.emptyList()).recordIds(Collections.singletonList(payrollDetailId)).build());
    }

    private void validateNoticeExists(Long id) {
        if (salaryNoticeMapper.selectById(id) == null) {
            throw exception(SALARY_NOTICE_NOT_EXISTS);
        }
    }

    private void validateNoticeIdAndEmployeeIdConsistence(Long id, Long employeeId) {
        SalaryNoticeDO notice = salaryNoticeMapper.selectById(id);
        if(Objects.isNull(notice)){
            throw exception(SALARY_NOTICE_NOT_EXISTS);
        }
        if(!notice.getEmployeeId().equals(employeeId)){
            throw exception(SALARY_NOTICE_EMPLOYEE_ID_NOT_CONSISTENCE);
        }
    }

    private void validateNoticeNotConfirm(Long id) {
        SalaryNoticeDO notice = salaryNoticeMapper.selectById(id);
        if(Objects.isNull(notice)){
            throw exception(SALARY_NOTICE_NOT_EXISTS);
        }
        if(Boolean.TRUE.equals(notice.getConfirmStatus())){
            throw exception(SALARY_NOTICE_CONFIRMED);
        }
    }

    private void validateNoticeNotConfirmByPayrollDetailId(Long payrollDetailId) {
        SalaryNoticeDO notice = salaryNoticeMapper.selectByPayrollDetailId(payrollDetailId);
        if(Objects.isNull(notice)){
            throw exception(SALARY_NOTICE_NOT_EXISTS);
        }
        if(Boolean.TRUE.equals(notice.getConfirmStatus())){
            throw exception(SALARY_NOTICE_CONFIRMED);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public SalaryNoticeDO getNotice(Long id) {
        SalaryNoticeDO notice = salaryNoticeMapper.selectById(id);
        if(Objects.isNull(notice)){
            return null;
        }
        payrollDetailService.updateReadStatus(notice.getPayrollDetailId());
        return notice;
    }

    @Override
    public PageResult<SalaryNoticeDO> getNoticePage(AppSalaryNoticePageReqVO pageReqVO) {
        return salaryNoticeMapper.selectPage(pageReqVO);
    }


}